package core;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;

import HECS.HECSContainerComponent;
import HECS.HECSEntity;
import HECS.HECSManager;
import components.BasicStatsComponent;
import components.TerrainComponent;
import components.GridPositionComponent;
import components.tile.ClimateComponent;
import components.tile.LandscapeComponent;
import components.tile.LandscapeGraphicComponent;
import components.tile.VegetationComponent;

public class GridManager extends HECSContainerComponent {
	public HashMap<Integer, HashMap<Integer, HashMap<Integer, HECSEntity>>> map;

	public void generateTile(HECSManager ecsManager, int x, int y, int z) {
		ClimateComponent climate = Constants.TEMPERATE;
		HECSEntity entity = ecsManager.createEntity();
		GridPositionComponent p = new GridPositionComponent(x, y, z);
		GridPositionComponent tile = new GridPositionComponent(x, y, z, climate, assignTerrain(x, y, z, climate));
		ClimateComponent c = new ClimateComponent(id, color);
		TerrainComponent t = new TerrainComponent(landtype, vegetation);
		
		
		addTileToMap(x, y, z, tile); // center
		generateTileZone(x, y, z);
		attach(entity);
	}

	/**
	 * Attempts to create new Tiles on all adjacent coordinates by the given origin coordinates.
	 */
	void generateTileZone(int x, int y, int z) {
		addTileToMap(x, y, z, createTile(x, y, z)); // center
		addTileToMap(x + 1, y, z, createTile(x + 1, y, z)); // west
		addTileToMap(x, y + 1, z, createTile(x, y + 1, z)); // north
		addTileToMap(x - 1, y, z, createTile(x - 1, y, z)); // east
		addTileToMap(x, y - 1, z, createTile(x, y - 1, z)); // south
	}

	/**
	 * Looks up an returns the Tile on the given coordinates. If there is non return null.
	 * 
	 * Use the boolean check (findTile(x, y, z) != null) to check if an Tile exists. If true, an Tile exists on the given coordinates.
	 * 
	 * @param map
	 */
	GridPositionComponent findTile(int x, int y, int z) {
		HashMap<Integer, HashMap<Integer, GridPositionComponent>> xmap = map.get(z);
		if (xmap == null)
			return null;
		HashMap<Integer, GridPositionComponent> ymap = xmap.get(x);
		if (ymap == null)
			return null;
		GridPositionComponent Tile = ymap.get(y);
		if (Tile == null)
			return null;
		return Tile;
	}

	/**
	 * Adds the given Tile to the map on the given coordinates. If an Tile already exist on the given coordinates it returns.
	 */
	private void addTileToMap(int x, int y, int z, GridPositionComponent tile) {
		if (findTile(x, y, z) != null)
			return;
		HashMap<Integer, HashMap<Integer, GridPositionComponent>> xmap = map.get(z);
		if (xmap == null) {
			xmap = new HashMap<Integer, HashMap<Integer, GridPositionComponent>>();
			map.put(z, xmap);
		}

		HashMap<Integer, GridPositionComponent> ymap = xmap.get(x);
		if (ymap == null) {
			ymap = new HashMap<Integer, GridPositionComponent>();
			xmap.put(x, ymap);
		}
		// System.out.println("new tile at: " + x + ":" + y);
		ymap.put(y, tile);

	}

	// INSERT PROBABILITY CALCULATIONS FOR CLIMATES !
	private GridPositionComponent createTile(int x, int y, int z) {
		ClimateComponent climate;
		climate = assignClimate(y);

		TerrainComponent terrain = assignTerrain(x, y, z, climate);

		GridPositionComponent Tile = new GridPositionComponent(x, y, z, climate, terrain);
		return Tile;

	}

	private TerrainComponent assignTerrain(int x, int y, int z, ClimateComponent climate) {
		VegetationComponent[] vegetationPossibilities = climate.vegetation;
		int r = Utility.rndInt(100);
		LandscapeGraphicComponent landtype = r < 50 ? Constants.GROUND : r < 80 ? Constants.HILLS : r < 90 ? Constants.MOUNTAINS : Constants.WATER;
		int sum = 0;
		ArrayList<VegetationComponent> acceptedVegetations = new ArrayList<VegetationComponent>();
		for (VegetationComponent v : vegetationPossibilities) {
			// if (v.vegetation.lsGround && landtype == Constants.GROUND || v.vegetation.lsHills && landtype == Constants.HILLS || v.vegetation.lsMountains && landtype == Constants.MOUNTAINS) {
			sum += v.rating;
			acceptedVegetations.add(v);

			// }
		}

		r = Utility.rndInt(sum);
		LandscapeComponent vegetation = null;
		int d = 0;
		for (VegetationComponent v : acceptedVegetations) {
			d += v.rating;
			if (r < d) {
				vegetation = v.vegetation;
				break;
			}

		}

		return new TerrainComponent(landtype, vegetation);
	}

	private ClimateComponent assignClimate(int y) {
		ClimateComponent climate;
		int noise = 10;
		int arctic_highlands = 10;
		int arctic = 20;
		int short_sub_arctic = 16;
		int sub_arctic = 30;
		int northern = 26;
		int temperate = 40;
		int mediterranean = 30;
		int arid = 40;
		int sub_Tropical = 20;
		int tropical = 30;

		int S = short_sub_arctic + (arctic_highlands / 2);
		int SN = S + northern;
		int SNT = SN + temperate;
		int SNTM = SNT + mediterranean;
		int SNTMA = SNTM + arid;
		int SNTMAS = SNTMA + sub_Tropical;
		int SNTMAST = SNTMAS + tropical;
		int SNTMASTS = SNTMAST + sub_Tropical;
		int SNTMASTSA = SNTMASTS + arid;
		int SNTMASTSAM = SNTMASTSA + mediterranean;
		int SNTMASTSAMT = SNTMASTSAM + temperate;
		int SNTMASTSAMTN = SNTMASTSAMT + northern;
		int SNTMASTSAMTNS = SNTMASTSAMTN + sub_arctic;
		int SNTMASTSAMTNSA = SNTMASTSAMTNS + arctic;

		if (y < arctic_highlands / 2 && -(arctic_highlands / 2) < y) {
			// climate = Constants.ARCTIC_HIGHLANDS; // This if there is no noise
			climate = noise_Addition(y, noise, arctic_highlands / 2, Constants.SUB_ARCTIC, Constants.NORTHERN);
		} else if (y < S && -S < y) {
			// climate = Constants.SUB_ARCTIC;
			climate = noise_Addition(y, noise, S, Constants.SUB_ARCTIC, Constants.NORTHERN);
		} else if (y < SN && -SN < y) {
			// climate = Constants.NORTHERN;
			climate = noise_Addition(y, noise, SN, Constants.NORTHERN, Constants.TEMPERATE);
		} else if (y < SNT && -SNT < y) {
			// climate = Constants.TEMPERATE;
			climate = noise_Addition(y, noise, SNT, Constants.TEMPERATE, Constants.MEDITERRANEAN);
		} else if (y < SNTM && -SNTM < y) {
			// climate = Constants.MEDITERRANEAN;
			climate = noise_Addition(y, noise, SNTM, Constants.MEDITERRANEAN, Constants.ARID);
		} else if (y < SNTMA && -SNTMA < y) {
			// climate = Constants.ARID;
			climate = noise_Addition(y, noise, SNTMA, Constants.ARID, Constants.SUB_TROPICAL);
		} else if (y < SNTMAS && -SNTMAS < y) {
			// climate = Constants.SUB_TROPICAL;
			climate = noise_Addition(y, noise, SNTMAS, Constants.SUB_TROPICAL, Constants.TROPICAL);
		} else if (y < SNTMAST && -SNTMAST < y) {
			// climate = Constants.TROPICAL;
			climate = noise_Addition(y, noise, SNTMAST, Constants.TROPICAL, Constants.SUB_TROPICAL);
		} else if (y < SNTMASTS && -SNTMASTS < y) {
			// climate = Constants.SUB_TROPICAL;
			climate = noise_Addition(y, noise, SNTMASTS, Constants.SUB_TROPICAL, Constants.ARID);
		} else if (y < SNTMASTSA && -SNTMASTSA < y) {
			// climate = Constants.ARID;
			climate = noise_Addition(y, noise, SNTMASTSA, Constants.ARID, Constants.MEDITERRANEAN);
		} else if (y < SNTMASTSAM && -SNTMASTSAM < y) {
			// climate = Constants.MEDITERRANEAN;
			climate = noise_Addition(y, noise, SNTMASTSAM, Constants.MEDITERRANEAN, Constants.TEMPERATE);
		} else if (y < SNTMASTSAMT && -SNTMASTSAMT < y) {
			// climate = Constants.TEMPERATE;
			climate = noise_Addition(y, noise, SNTMASTSAMT, Constants.TEMPERATE, Constants.NORTHERN);
		} else if (y < SNTMASTSAMTN && -SNTMASTSAMTN < y) {
			// climate = Constants.NORTHERN;
			climate = noise_Addition(y, noise, SNTMASTSAMTN, Constants.NORTHERN, Constants.SUB_ARCTIC);
		} else if (y < SNTMASTSAMTNS && -SNTMASTSAMTNS < y) {
			// climate = Constants.SUB_ARCTIC;
			climate = noise_Addition(y, noise, SNTMASTSAMTNS, Constants.SUB_ARCTIC, Constants.ARCTIC);
		} else if (y < SNTMASTSAMTNSA && -SNTMASTSAMTNSA < y) {
			// climate = Constants.ARCTIC;
			climate = noise_Addition(y, noise, SNTMASTSAMTNSA, Constants.ARCTIC, Constants.EXTREME_ARCTIC);
		} else {
			climate = Constants.EXTREME_ARCTIC;
		}
		return climate;
	}

	private ClimateComponent noise_Addition(int y, int noise, int S, ClimateComponent Climate1, ClimateComponent Climate2) {
		ClimateComponent climate;
		Random randomGenerator = new Random();
		if (y < S - noise && -S + noise < y) {
			climate = Climate1;
		} else {
			if (randomGenerator.nextInt(2) == 1) {
				climate = Climate1;
			} else {
				climate = Climate2;
			}
		}
		return climate;
	}

	public void render(int xpos, int ypos, int zpos, Graphics2D g) {
		Color c;
		int xsize = 40;
		int ysize = 30;
		int width = xsize * Constants.SCALE;
		int height = ysize * Constants.SCALE;
		for (int x = -xsize; x < xsize; x++) {
			for (int y = ysize; y >= -ysize; y--) {
				int xscreen = x * Constants.SCALE + Window.getInstance().getWidth() / 2;
				int yscreen = -y * Constants.SCALE + Window.getInstance().getHeight() / 2;

				GridPositionComponent tile = findTile(xpos + x, ypos + y, zpos);
				if (tile == null) {
					g.setColor(Color.BLACK);
					g.drawRect(xscreen, yscreen, Constants.SCALE, Constants.SCALE);
					continue;
				}
				g.setColor(tile.climate.color);
				g.fillRect(xscreen, yscreen, Constants.SCALE, Constants.SCALE);
				g.setColor(Color.BLACK);
				g.drawRect(xscreen, yscreen, Constants.SCALE, Constants.SCALE);
				g.setColor(Color.black);
				g.drawString(tile.terrain.vegetation.graphic, xscreen + 1, yscreen + 20);
				g.drawString(tile.terrain.landtype.graphic, xscreen + 1, yscreen + 10);
				// g.drawString(Integer.toString(tile.x) + ":" + Integer.toString(tile.y), xscreen + 2, yscreen + 14);
			}
		}
		g.setColor(Color.BLACK);
		g.drawRect(-width + Window.getInstance().getWidth() / 2, -height + Window.getInstance().getHeight() / 2, width * 2, height * 2);

	}

	@Override
	protected void removed(HECSManager manager, HECSEntity entity) {
		// TODO Auto-generated method stub

	}

}

// findTile(x + 1, y, z); // east
// findTile(x + 1, y + 1, z); // north-east
// findTile(x, y + 1, z); // north
// findTile(x - 1, y + 1, z); // north-west
// findTile(x - 1, y, z); // west
// findTile(x - 1, y - 1, z); // south-west
// findTile(x, y - 1, z); // south
// findTile(x + 1, y - 1, z); // south-east

// for (Entry<Integer, HashMap<Integer, Tile>> entryX : map.get(zpos).entrySet()) {
// for (Entry<Integer, Tile> entryY : entryX.getValue().entrySet()) {
//
// // int xscreen = entryX.getKey() * Constants.SCALE - x * Constants.SCALE;
// // int yscreen = entryY.getKey() * Constants.SCALE - y * Constants.SCALE;
// // g.setColor(entryY.getValue().);
// int xscreen = entryX.getKey() * Constants.SCALE - xpos * Constants.SCALE + Window.getInstance().getWidth() / 2;
// int yscreen = -entryY.getKey() * Constants.SCALE + ypos * Constants.SCALE + Window.getInstance().getHeight() / 2;
// g.fillRect(xscreen, yscreen, Constants.SCALE, Constants.SCALE);
// System.out.println(xscreen + " " + yscreen);
// }
// }